using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using log4net;
using System.Reflection;

namespace Common.Shared.IO {
	/// <summary>
	/// Monitors a file for specific "change" events
	/// </summary>
	public sealed class FileWatcher {
		private static readonly ILog Log = LogManager.GetLogger ( MethodBase.GetCurrentMethod ( ).DeclaringType );

		/// <summary>
		/// Occurs when the file was created
		/// </summary>
		public event EventHandler<FileSystemEventArgs> Created;
		/// <summary>
		/// Occurs when the file has changed
		/// </summary>
		public event EventHandler<FileSystemEventArgs> Changed;
		/// <summary>
		/// Occurs when the file was deleted
		/// </summary>
		public event EventHandler<FileSystemEventArgs> Deleted;
		/// <summary>
		/// Occurs when there was an error
		/// </summary>
		public event EventHandler<ErrorEventArgs> Error;

		private FileInfo _file = null;
		private FileSystemWatcher _watcher = null;
		private bool _enabled = false;

		/// <summary>
		/// Initializes a new instance of the <see cref="FileWatcher"/> class.
		/// </summary>
		/// <param name="file">The file.</param>
		public FileWatcher ( FileInfo file ) {
			this.File = file;
			if ( !file.Directory.Exists ) {
				try {
					file.Directory.Create ( );
				} catch ( Exception ex ) {
					Log.Error ( ex.Message, ex );
				}
			}
			this.Watcher = new FileSystemWatcher ( file.Directory.FullName, file.Name );
			this.Watcher.EnableRaisingEvents = false;
			this.Watcher.IncludeSubdirectories = false;
			this.Watcher.NotifyFilter = NotifyFilters.Size | NotifyFilters.FileName | NotifyFilters.CreationTime;
			this.Watcher.Changed += new FileSystemEventHandler ( OnChanged );
			this.Watcher.Created += new FileSystemEventHandler ( OnCreated );
			this.Watcher.Deleted += new FileSystemEventHandler ( OnDeleted );
			this.Watcher.Error += new ErrorEventHandler ( OnError );
		}

		/// <summary>
		///  Raises the <see cref="E:Error"/> event.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.IO.ErrorEventArgs"/> instance containing the event data.</param>
		private void OnError ( object sender, ErrorEventArgs e ) {
			if ( this.Error != null ) {
				this.Error ( this, e );
			}
		}

		/// <summary>
		///  Raises the <see cref="E:Deleted"/> event.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.IO.FileSystemEventArgs"/> instance containing the event data.</param>
		private void OnDeleted ( object sender, FileSystemEventArgs e ) {
			if ( string.Compare ( e.FullPath, this.File.FullName, true ) == 0 ) {
				if ( this.Deleted != null ) {
					this.Deleted ( this, e );
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="E:Created"/> event.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.IO.FileSystemEventArgs"/> instance containing the event data.</param>
		private void OnCreated ( object sender, FileSystemEventArgs e ) {
			if ( string.Compare ( e.FullPath, this.File.FullName, true ) == 0 ) {
				if ( this.Created != null ) {
					this.Created ( this, e );
				}
			}
		}

		/// <summary>
		///  Raises the <see cref="E:Changed"/> event.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.IO.FileSystemEventArgs"/> instance containing the event data.</param>
		private void OnChanged ( object sender, FileSystemEventArgs e ) {
			if ( string.Compare ( e.FullPath, this.File.FullName, true ) == 0 ) {
				if ( this.Changed != null ) {
					this.Changed ( this, e );
				}
			}
		}

		/// <summary>
		/// Gets or sets the watcher.
		/// </summary>
		/// <value>The watcher.</value>
		private FileSystemWatcher Watcher {
			get { return this._watcher; }
			set { this._watcher = value; }
		}

		/// <summary>
		/// Getsthe file.
		/// </summary>
		/// <value>The file.</value>
		public FileInfo File {
			get { return this._file; }
			private set { this._file = value; }
		}

		/// <summary>
		/// Gets or sets a value indicating whether this <see cref="FileWatcher"/> is enabled.
		/// </summary>
		/// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
		public bool Enabled {
			get { return this._enabled; }
			private set { this._enabled = value; }
		}

		/// <summary>
		/// Starts monitoring the file.
		/// </summary>
		public void Start ( ) {
			this.Watcher.EnableRaisingEvents = this.Enabled = true;
		}

		/// <summary>
		/// Stops (or pauses) the monitoring of the file.
		/// </summary>
		public void Stop ( ) {
			this.Watcher.EnableRaisingEvents = this.Enabled = false;
		}
	}
}

